﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;

using Castle.DynamicProxy;
using EntityAop.Attributes;
using EntityAop.Configuration;
using EntityAop.Extensions;
using EntityAop.Interceptors;
using EntityAop.Interceptors.InterfacesToImplement;
using EntityAop.Interceptors.Support;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RrslLite.Configuration;
using ProxyGenerator = EntityAop.ProxyGenerator;

namespace App.Test.RrslLiteTEST
{
    /// <summary>
    /// Summary description for EntityAopTEST
    /// </summary>
    [TestClass]
    public class EntityAopTEST
    {
        private static EntityAopConfiguration EntityAopProxyGeneratorConfiguration
        {
            get { return RrslLiteConfiguration.Instance.BatchBuilderConfiguration.EntityAopProxyGeneratorConfiguration; }
        }

        private static ClassToBeProxied GetInstanceToBeProxied()
        {
            var instanceToBeProxied = new ClassToBeProxied();

            instanceToBeProxied.PropertyToBeProxied = "some";

            instanceToBeProxied.SomeInt = 99;

            instanceToBeProxied.MemberClassToBeProxied =
                new MemberClassToBeProxied() { PropertyToBeProxied = "some" };

            instanceToBeProxied.MemberClassToBeProxiedIList =
                new List<MemberClassToBeProxied>()
                    {
                        new MemberClassToBeProxied(){ PropertyToBeProxied = "some1"},
                        new MemberClassToBeProxied(){ PropertyToBeProxied = "some2"},
                        new MemberClassToBeProxied(){ PropertyToBeProxied = "some3"}
                    };

            instanceToBeProxied.ListOfStrings = new List<string>()
                                                    {
                                                        "some",
                                                        "some1",
                                                        "some3"
                                                    };

            instanceToBeProxied.ByteArray = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
            instanceToBeProxied.StringArray = new string[] { "some1", "some2", "some3", "some4" };

            return instanceToBeProxied;
        }

        private static ProxyGenerator proxyGenerator;
        private static ProxyGenerator GetProxyGenerator()
        {
            if (proxyGenerator == null)
            {
                proxyGenerator = EntityAopProxyGeneratorConfiguration.ProxyGeneratorCreateFunc();
            }

            return proxyGenerator;
        }

        private static ClassToBeProxied GetProxiedInstance()
        {
            var instanceToBeProxied = GetInstanceToBeProxied();

            var proxiedInstance = EntityAopProxyGeneratorConfiguration.CreateProxiedInstance(instanceToBeProxied);

            return proxiedInstance;
        }

        private static IList<ClassToBeProxied> GetCollectionToBeProxied()
        {
            return GetCollectionToBeProxied(500);
        }

        private static IList<ClassToBeProxied> GetCollectionToBeProxied(int itemCount)
        {
            var result = new List<ClassToBeProxied>();

            for (int i = 0; i < itemCount; i++)
            {
                var item = GetInstanceToBeProxied();

                item.PropertyToBeProxied = string.Format("{0}th instance", i);

                result.Add(item);
            }

            return result;
        }

        private static IList<ClassToBeProxied> GetProxiedCollection()
        {
            return GetProxiedCollection(GetCollectionToBeProxied());
        }

        private static ObservableCollection<ClassToBeProxied>  GetProxiedObservableCollection()
        {
            return (ObservableCollection<ClassToBeProxied>) GetProxiedCollection();
        }

        private static IList<ClassToBeProxied> GetProxiedCollection(IList<ClassToBeProxied> collectionToBeProxied)
        {
            var proxiedInstance = EntityAopProxyGeneratorConfiguration.CreateProxiedInstance(collectionToBeProxied);

            return proxiedInstance;
        }

        [TestInitialize]
        public void Init()
        {
            RrslLiteConfiguration.Instance.WithDefaultBatchBuilderConfiguration();

            RrslLiteConfiguration.Instance.BatchBuilderConfiguration
                .WithCreateClassProxyWithTargetRecursiveToReceiveMessages()
                .WithINotifyPropertyChangedInterceptorToReceiveMessages()
                ;
        }

        [TestMethod]
        public void EntityAop_CreateProxy()
        {
            var proxiedInstance = GetProxiedInstance();

            Assert.IsNotNull(proxiedInstance);

            Assert.IsNotNull(proxiedInstance.PropertyToBeProxied);
            Assert.IsNotNull(proxiedInstance.MemberClassToBeProxied);
            Assert.IsNotNull(proxiedInstance.MemberClassToBeProxiedIList);

            var instanceAsNotifyPropertyChanged = proxiedInstance as INotifyPropertyChanged;

            Assert.IsNotNull(instanceAsNotifyPropertyChanged);

            var propertyWasChanged = false;

            var newValue = "some new value";

            instanceAsNotifyPropertyChanged.PropertyChanged +=
                (s, e) =>
                {
                    var classToBeProxied = (ClassToBeProxied)s;

                    Assert.AreEqual(newValue, classToBeProxied.PropertyToBeProxied);
                    propertyWasChanged = true;
                };

            proxiedInstance.PropertyToBeProxied = newValue;

            Assert.IsTrue(propertyWasChanged);
        }

        [TestMethod]
        public void EntityAop_CreateProxy_And_Check_NonPrimitive_Properties_Are_Proxies_Also()
        {
            var proxiedInstance = GetProxiedInstance();

            var memberInstanceAsNotifyPropertyChanged = proxiedInstance.MemberClassToBeProxied as INotifyPropertyChanged;

            Assert.IsNotNull(memberInstanceAsNotifyPropertyChanged);
        }

        [TestMethod]
        public void EntityAop_CreateProxy_And_Check_Collection_Properties_Are_Proxies()
        {
            var proxyGenerator = GetProxyGenerator();

            var proxiedInstance = GetProxiedInstance();

            var memberClassToBeProxiedIList = proxiedInstance.MemberClassToBeProxiedIList as INotifyPropertyChanged;

            Assert.IsTrue(proxyGenerator.IsInstanceProxied(proxiedInstance));

            Assert.IsNotNull(memberClassToBeProxiedIList);

            var itemInListAsINotifyPropertyChanged = proxiedInstance.MemberClassToBeProxiedIList.First() as INotifyPropertyChanged;

            Assert.IsNotNull(itemInListAsINotifyPropertyChanged);
        }

        [TestMethod]
        public void EntityAop_CreateProxy_From_Collection()
        {
            var proxyGenerator = GetProxyGenerator();

            var proxiedCollection = GetProxiedCollection();

            Assert.IsFalse(proxyGenerator.IsInstanceProxied(proxiedCollection));

            Assert.IsInstanceOfType(proxiedCollection, typeof(INotifyPropertyChanged));

            Assert.IsInstanceOfType(proxiedCollection.First(), typeof(INotifyPropertyChanged));

            Assert.IsInstanceOfType(proxiedCollection.First().MemberClassToBeProxied, typeof(INotifyPropertyChanged));

            Assert.IsInstanceOfType(proxiedCollection.First().MemberClassToBeProxiedIList,
                                    typeof(INotifyPropertyChanged));
        }

        [TestMethod]
        public void EntityAop_UnProxy_NonProxied()
        {
            var notProxiedInstance = GetInstanceToBeProxied();

            var proxyGenerator = new ProxyGenerator();

            var unProxiedInstance = notProxiedInstance.Unproxy();

            Assert.AreEqual(notProxiedInstance, unProxiedInstance);
        }

        [TestMethod]
        public void EntityAop_TargetAccessorInterceptor()
        {
            var proxied = GetProxiedInstance();

            var proxiedAsTargetAccessor = proxied as ITargetAccessor;

            Assert.IsNotNull(proxiedAsTargetAccessor);

            var unproxied = proxiedAsTargetAccessor.GetTarget<ClassToBeProxied>();

            Assert.IsNotInstanceOfType(unproxied, typeof(ITargetAccessor));

            var unproxiedNonGeneric = proxiedAsTargetAccessor.GetTarget();

            Assert.IsNotInstanceOfType(unproxiedNonGeneric, typeof(ITargetAccessor));
        }

        [TestMethod]
        public void EntityAop_UnProxy_Instance()
        {
            var proxiedInstance = GetProxiedInstance();

            var proxyGenerator = GetProxyGenerator();

            var unProxiedInstance = proxiedInstance.Unproxy();

            Assert.IsFalse(proxyGenerator.IsInstanceProxied(unProxiedInstance));

            Assert.IsNotInstanceOfType(unProxiedInstance, typeof(INotifyPropertyChanged));
            Assert.IsNotInstanceOfType(unProxiedInstance.MemberClassToBeProxiedIList, typeof(INotifyPropertyChanged));
            Assert.IsNotInstanceOfType(unProxiedInstance.MemberClassToBeProxiedIList.First(), typeof(INotifyOfPropertyChange));

            var anotherProxiedInstance = GetProxiedInstance();

            var anotherUnProxiedInstance = proxyGenerator.Unproxy(anotherProxiedInstance);

            Assert.IsFalse(proxyGenerator.IsInstanceProxied(anotherUnProxiedInstance));

            Assert.IsNotInstanceOfType(anotherUnProxiedInstance, typeof(INotifyPropertyChanged));
        }

        [TestMethod]
        public void EntityAop_UnProxy_Collection()
        {
            var proxiedCollection = GetProxiedObservableCollection();

            var proxyGenerator = GetProxyGenerator();

            var unProxiedCollection = proxiedCollection.Unproxy();

            Assert.IsNotInstanceOfType(unProxiedCollection, typeof(INotifyPropertyChanged));
            Assert.IsNotInstanceOfType(unProxiedCollection.First(), typeof(INotifyPropertyChanged));

            Assert.IsNotInstanceOfType(unProxiedCollection.First().MemberClassToBeProxied, typeof(INotifyPropertyChanged));
            Assert.IsNotInstanceOfType(unProxiedCollection.First().MemberClassToBeProxiedIList.First(), typeof(INotifyPropertyChanged));
        }

        [TestMethod]
        public void EntityAop_UnProxy_Huge_Measure_Time_And_Memory_Effort_And_Compare_With_Normal_Creation()
        {
            var currentTime = DateTime.Now;

            var collectionToBeProxied = GetCollectionToBeProxied(50000);

            var collectionCreatitionTime = DateTime.Now - currentTime;
            currentTime = DateTime.Now;

            var proxiedCollection = GetProxiedCollection(collectionToBeProxied);

            var proxyCreationTime = DateTime.Now - currentTime;

            var proxyGenerator = GetProxyGenerator();

            currentTime = DateTime.Now;
            var unProxiedCollection = proxiedCollection.Unproxy();

            var unProxyTime = DateTime.Now - currentTime;

            Console.WriteLine(string.Format("collectionCreatitionTime {0}", collectionCreatitionTime.TotalSeconds));
            Console.WriteLine(string.Format("proxyCreationTime {0}", proxyCreationTime.TotalSeconds));
            Console.WriteLine(string.Format("unProxyTime {0}", unProxyTime.TotalSeconds));
        }

        [TestMethod]
        public void EntityAop_CreateProxy_Huge_Measure_Time_And_Memory_Effort_And_Compare_With_Normal_Creation()
        {
            var currentTime = DateTime.Now;

            var collectionToBeProxied = GetCollectionToBeProxied(5000);

            var collectionCreatitionTime = DateTime.Now - currentTime;
            currentTime = DateTime.Now;

            var proxiedCollection = GetProxiedCollection(collectionToBeProxied);

            var proxyCreationTime = DateTime.Now - currentTime;

            Console.WriteLine(string.Format("collectionCreatitionTime {0}", collectionCreatitionTime.TotalSeconds));
            Console.WriteLine(string.Format("proxyCreationTime {0}", proxyCreationTime.TotalSeconds));

        }

        [TestMethod]
        public void EntityAOP_CreateProxy_With_Byte_And_String_Array_It_Is_Not_Proxied()
        {
            var proxied = GetProxiedInstance();

            Assert.IsNotInstanceOfType(proxied.ByteArray, typeof(INotifyPropertyChanged));
            Assert.IsNotInstanceOfType(proxied.StringArray, typeof(INotifyPropertyChanged));
        }

        [TestMethod]
        public void EntityAOP_CreateProxy_Same_Value_Will_Not_Fire_NPC()
        {
            var proxied = GetProxiedInstance();

            bool propertyChangedWasFired = false;

            ((INotifyPropertyChanged)proxied).PropertyChanged +=
                (s, e) =>
                {
                    propertyChangedWasFired = true;
                };

            proxied.PropertyToBeProxied = proxied.PropertyToBeProxied;

            Assert.IsFalse(propertyChangedWasFired);

            proxied.PropertyToBeProxied = "new value #$#$";

            Assert.IsTrue(propertyChangedWasFired);
        }

        [TestMethod]
        public void EntityAOP_CreateProxy_INotifyPropertyChange_Fires_Only_In_Affected_Instance()
        {
            var proxied1 = GetProxiedInstance();
            var proxied2 = GetProxiedInstance();

            Assert.IsTrue(proxied1 != proxied2);

            var proxiedAsINotifyOfPropertyChange1 = proxied1 as INotifyOfPropertyChange;
            var proxiedAsINotifyOfPropertyChange2 = proxied2 as INotifyOfPropertyChange;
            bool isFiredOPC1 = false;
            bool isFiredOPC2 = false;

            Assert.IsNotNull(proxiedAsINotifyOfPropertyChange1);
            Assert.IsNotNull(proxiedAsINotifyOfPropertyChange2);
            ((INotifyPropertyChanged)proxied1).PropertyChanged += (s, e) =>
                                                    {
                                                        isFiredOPC1 = true;
                                                    };
            ((INotifyPropertyChanged)proxied2).PropertyChanged += (s, e) =>
            {
                isFiredOPC2 = true;
            };

            proxied1.PropertyToBeProxied = "New value";

            Assert.IsTrue(isFiredOPC1);
            Assert.IsFalse(isFiredOPC2);

        }

        [TestMethod]
        public void EntityAOP_CreateProxy_Fills_The_EntityMetadata()
        {
            var proxied = GetProxiedInstance();

            var dummy = proxied.GetHashCode();

            var asProxyTargetAccessor = (proxied as IProxyTargetAccessor);

            Assert.IsNotNull(asProxyTargetAccessor);

            var INotifyPropertyChangedInterceptorType = typeof(InstanceSpecificInterceptor);

            var iNotifyPropertyChangedInterceptor =
                asProxyTargetAccessor
                .GetInterceptors()
                .FirstOrDefault(interceptor => interceptor.GetType() == INotifyPropertyChangedInterceptorType)
                as InstanceSpecificInterceptor;

            Assert.IsNotNull(iNotifyPropertyChangedInterceptor);

            var metadata = EntityMetadataHelper.GetEntityMetadata(proxied.GetType().BaseType);

            var nonPerzistentPropertyDependsOn = metadata.GetDependsOn("NonPerzistentProperty").ToArray();

            Assert.IsNotNull(nonPerzistentPropertyDependsOn);
            Assert.IsTrue(nonPerzistentPropertyDependsOn.Count() > 0);
            Assert.AreEqual("PropertyToBeProxied", nonPerzistentPropertyDependsOn.First().Value);

            var propertyToBeProxiedDependencies = metadata.GetDependencies("PropertyToBeProxied");

            Assert.IsNotNull(propertyToBeProxiedDependencies);
            Assert.IsTrue(propertyToBeProxiedDependencies.Count() > 0);
            Assert.AreEqual("NonPerzistentProperty", propertyToBeProxiedDependencies.First().Value);
        }

        [TestMethod]
        public void EntityAOP_CreateProxy_INotifyOfPropertyChange_Of_NonperzistentProperty()
        {
            var proxied = GetProxiedInstance();

            var proxiedAsINotifyOfPropertyChange = proxied as INotifyOfPropertyChange;

            Assert.IsNotNull(proxiedAsINotifyOfPropertyChange);

            bool PropertyToBeProxiedWasChanged = false;

            bool NonPerzistentPropertyWasChanged = false;
            bool NonPerzistentProperty2WasChanged = false;
            bool anotherPropertyWasNotified = false;


            ((INotifyPropertyChanged)proxied).PropertyChanged +=
                (s, e) =>
                {
                    if (e.PropertyName == "PropertyToBeProxied")
                    {
                        PropertyToBeProxiedWasChanged = true;
                        return;
                    }

                    if (e.PropertyName == "NonPerzistentProperty")
                    {
                        NonPerzistentPropertyWasChanged = true;
                        return;
                    }

                    if (e.PropertyName == "NonPerzistentProperty2")
                    {
                        NonPerzistentProperty2WasChanged = true;
                        return;
                    }

                    anotherPropertyWasNotified = true;
                };

            Assert.IsFalse(PropertyToBeProxiedWasChanged);
            Assert.IsFalse(NonPerzistentPropertyWasChanged);
            Assert.IsFalse(NonPerzistentProperty2WasChanged);

            //proxiedAsINotifyOfPropertyChange.NotifyOfPropertyChange(proxied as INotifyOfPropertyChange, "ListOfStrings");

            proxied.PropertyToBeProxied = "new value";

            Assert.IsTrue(PropertyToBeProxiedWasChanged);
            Assert.IsTrue(NonPerzistentPropertyWasChanged);
            Assert.IsTrue(NonPerzistentProperty2WasChanged);

            Assert.IsFalse(anotherPropertyWasNotified);
        }

        [TestMethod]
        public void EntityAOP_CreateProxy_INotifyOfPropertyChange_Of_ChangeMethod()
        {
            var proxied = GetProxiedInstance();

            var proxiedAsINotifyOfPropertyChange = proxied as INotifyOfPropertyChange;

            Assert.IsNotNull(proxiedAsINotifyOfPropertyChange);

            bool PropertyToBeProxiedWasChanged = false;
            bool NonPerzistentPropertyWasChanged = false;
            bool NonPerzistentProperty2WasChanged = false;
            bool anotherPropertyWasNotified = false;

            ((INotifyPropertyChanged)proxied).PropertyChanged +=
                (s, e) =>
                {
                    if (e.PropertyName == "PropertyToBeProxied")
                    {
                        PropertyToBeProxiedWasChanged = true;

                        Assert.AreEqual(null, proxied.PropertyToBeProxied);

                        return;
                    }

                    if (e.PropertyName == "NonPerzistentProperty")
                    {
                        NonPerzistentPropertyWasChanged = true;

                        Assert.AreEqual(null, proxied.NonPerzistentProperty);

                        return;
                    }

                    if (e.PropertyName == "NonPerzistentProperty2")
                    {
                        NonPerzistentProperty2WasChanged = true;

                        Assert.AreEqual(null, proxied.NonPerzistentProperty2);

                        return;
                    }

                    anotherPropertyWasNotified = true;
                };

            Assert.IsFalse(PropertyToBeProxiedWasChanged);
            Assert.IsFalse(NonPerzistentPropertyWasChanged);
            Assert.IsFalse(NonPerzistentProperty2WasChanged);

            Assert.IsFalse(anotherPropertyWasNotified);

            proxied.ResetPropertyToBeProxied();

            Assert.IsTrue(PropertyToBeProxiedWasChanged);
            Assert.IsTrue(NonPerzistentPropertyWasChanged);
            Assert.IsTrue(NonPerzistentProperty2WasChanged);

            Assert.IsFalse(anotherPropertyWasNotified);

        }

        [TestMethod]
        public void EntityAOP_CreateProxy_INotifyOfPropertyChange_RegisterInverseReference()
        {
            var proxied = GetProxiedInstance();
            var proxiedTarget = InvocationHelper.GetTargetInvocationDirectlyFromProxy(proxied);

            var proxiedMemberAsINotifyOfPropertyChange = proxied.MemberClassToBeProxied as INotifyOfPropertyChange;
            var proxiedMemberTarget = InvocationHelper.GetTargetInvocationDirectlyFromProxy(proxied.MemberClassToBeProxied);

            Assert.IsNotNull(proxiedMemberAsINotifyOfPropertyChange);

            var proxiedTargetInstanceSpecificData = InstanceSpecificHelper.GetInstanceSpecificData(proxiedTarget.GetHashCode());

            proxiedTargetInstanceSpecificData.RegisterInstanceToInverseReference(proxiedTarget.GetType(), proxiedTarget.GetHashCode());

            var memberMetadata = EntityMetadataHelper.GetEntityMetadata(proxiedMemberTarget.GetType());

            var memberMetadataDependency = memberMetadata.GetDependencies("PropertyToBeProxied").First();

            Assert.IsTrue(memberMetadataDependency.IsAnotherInstanceDependent);
            Assert.IsTrue(memberMetadataDependency.Value == proxiedTarget.GetType().FullName + "|" + "NonPerzistentProperty3");
        }

        [TestMethod]
        public void EntityAOP_CreateProxy_INotifyOfPropertyChange_ReRegisterInverseReference_On_Member_Change()
        {
            var proxied = GetProxiedInstance();

            var proxiedTargetType =
                InvocationHelper.GetTargetInvocationDirectlyFromProxy(proxied).GetType();

            var oldProxiedInstanceSpecificData =
                InstanceSpecificHelper.GetInstanceSpecificData(proxied.MemberClassToBeProxied.GetHashCode());

            var newProxiedMemberClass =
                EntityAopProxyGeneratorConfiguration.CreateProxiedInstance(new MemberClassToBeProxied() { PropertyToBeProxied = "just some value" });

            var newProxiedInstanceSpecificData = InstanceSpecificHelper.GetInstanceSpecificData(newProxiedMemberClass.GetHashCode());

            Assert.AreEqual(0, newProxiedInstanceSpecificData.GetRegisteredInverseReferences(proxiedTargetType.FullName).Count());
            Assert.AreEqual(1, oldProxiedInstanceSpecificData.GetRegisteredInverseReferences(proxiedTargetType.FullName).Count());

            proxied.MemberClassToBeProxied = newProxiedMemberClass;

            Assert.AreEqual(1, newProxiedInstanceSpecificData.GetRegisteredInverseReferences(proxiedTargetType.FullName).Count());
            Assert.AreEqual(0, oldProxiedInstanceSpecificData.GetRegisteredInverseReferences(proxiedTargetType.FullName).Count());
        }

        [TestMethod]
        public void EntityAOP_CreateProxy_INotifyOfPropertyChange_Of_NonPerzistentProperty_Counted_From_MemberClass()
        {
            var proxied = GetProxiedInstance();

            var NonPerzistentProperty2WasChanged = false;
            var anotherPropertyWasNotified = false;

            var newValue = "new value xxx";

            ((INotifyPropertyChanged)proxied).PropertyChanged +=
                (s, e) =>
                {
                    if (e.PropertyName == "NonPerzistentProperty3")
                    {
                        NonPerzistentProperty2WasChanged = true;

                        Assert.AreEqual(newValue, proxied.NonPerzistentProperty3);

                        return;
                    }

                    anotherPropertyWasNotified = true;
                };

            var proxiedMember = proxied.MemberClassToBeProxied;
            var proxiedMembersPropertyToBeProxiedWasChanged = false;

            ((INotifyPropertyChanged)proxiedMember).PropertyChanged +=
                (s, e) =>
                {
                    if (e.PropertyName == "PropertyToBeProxied")
                    {
                        proxiedMembersPropertyToBeProxiedWasChanged = true;

                        Assert.AreEqual(newValue, proxied.MemberClassToBeProxied.PropertyToBeProxied);

                        return;
                    }

                    anotherPropertyWasNotified = true;
                };

            proxied.MemberClassToBeProxied.PropertyToBeProxied = newValue;

            Assert.IsFalse(anotherPropertyWasNotified);
            Assert.IsTrue(proxiedMembersPropertyToBeProxiedWasChanged);
            Assert.IsTrue(NonPerzistentProperty2WasChanged);
        }

        [TestMethod]
        public void EntityAOP_CreateProxy_And_Check_Equals()
        {
            var proxied = GetProxiedInstance();

            Assert.IsTrue(proxied.Equals(proxied));

            Assert.IsFalse(proxied.Equals(null));
        }

        [TestMethod]
        public void EntityAOP_Perzist_Created_Proxy()
        {
            var savePhysicalAssembly = true;
            var disableSignedModule = false;
            var strongAssemblyName = ModuleScope.DEFAULT_ASSEMBLY_NAME;
            var strongModulePath = ModuleScope.DEFAULT_FILE_NAME;
            var weakAssemblyName = "Foo.Bar.Proxies";
            var weakModulePath = "Foo.Bar.Proxies.dll";
            var scope = new ModuleScope(savePhysicalAssembly, disableSignedModule, strongAssemblyName, strongModulePath, weakAssemblyName, weakModulePath);
            var builder = new DefaultProxyBuilder(scope);
            //var generator = new ProxyGenerator(builder);

            EntityAopProxyGeneratorConfiguration.WithProxyGeneratorCreateFunc(() => new ProxyGenerator(builder));

            var proxied = GetProxiedInstance();

            scope.SaveAssembly(false);
        }

        public class ClassToBeProxied : ClassToBeProxiedBase
        {
            [DependsOn("PropertyToBeProxied")]
            public override string NonPerzistentProperty
            {
                get
                {
                    if (string.IsNullOrEmpty(base.NonPerzistentProperty))
                    {
                        return null;
                    }

                    return base.NonPerzistentProperty + " some additional crap";
                }
            }
        }

        public class ClassToBeProxiedBase
        {
            public virtual IList<String> ListOfStrings { get; set; }

            public virtual int SomeInt { get; set; }

            public virtual byte[] ByteArray { get; set; }

            public virtual string[] StringArray { get; set; }

            [DependsOn("PropertyToBeProxied")]
            public virtual string NonPerzistentProperty
            {
                get
                {
                    if (string.IsNullOrEmpty(PropertyToBeProxied))
                    {
                        return null;
                    }

                    return PropertyToBeProxied + "  additional";
                }
            }

            [DependsOn("NonPerzistentProperty")]
            public virtual string NonPerzistentProperty2
            {
                get
                {
                    if (string.IsNullOrEmpty(NonPerzistentProperty))
                    {
                        return null;
                    }
                    return NonPerzistentProperty + " add2";
                }
            }

            public virtual string PropertyToBeProxied { get; set; }

            public virtual MemberClassToBeProxied MemberClassToBeProxied { get; set; }

            public virtual IList<MemberClassToBeProxied> MemberClassToBeProxiedIList { get; set; }

            [Affects("PropertyToBeProxied")]
            public virtual void ResetPropertyToBeProxied()
            {
                PropertyToBeProxied = null;
            }

            [DependsOn("MemberClassToBeProxied.PropertyToBeProxied")]
            public virtual string NonPerzistentProperty3 { get { return MemberClassToBeProxied.PropertyToBeProxied; } }
        }

        public class MemberClassToBeProxied
        {
            public virtual string PropertyToBeProxied { get; set; }
        }
    }
}
